from Config.myConstant import *
from Config.myConfig import *
from joblib import Parallel, delayed,parallel_backend
from DataAccess.TradedayDataProcess import *
import redis
import numpy as np
import pandas as pd
import warnings
########################################################################
class analysis20191220():

    #----------------------------------------------------------------------
    def __init__(self):
        pass
    #----------------------------------------------------------------------
    def dataPreparedByCode(self,codes,redis_key_list):
        self.raw=[]
        for key in redis_key_list:
            data0=self.getDataFromRedis(key)
            self.raw.append(data0)
        self.raw=pd.concat(self.raw)
        n=len(codes)
        i=0
        if (self.raw.shape[0]>0):
            for code in codes:
                data=self.raw[self.raw['code']==code]
                self.deleteDataFromRedis(str(code))
                self.saveDataToRedis(data,str(code))
                i=i+1
                print(f"{code} compelete {i}/{n}")
    #----------------------------------------------------------------------
    def deleteDataFromRedis(self,redis_key):
        redis_client = redis.StrictRedis(host='127.0.0.1')
        data=redis_client.delete(redis_key)
        
    #----------------------------------------------------------------------
    def getDataFromRedis(self,redis_key):
        redis_client = redis.StrictRedis(host='127.0.0.1')
        data=redis_client.get(redis_key)
        if data!=None:
            data=pd.read_msgpack(data)
        else:
            data=pd.DataFrame()
        return data
    #----------------------------------------------------------------------
    def saveDataToRedis(self,data,redis_key):
        redis_client = redis.StrictRedis(host='127.0.0.1')
        modifiedData = data.to_msgpack()
        redis_client.set(redis_key, modifiedData)
        pass
    def getBestPerformanceByCodeGroup(self,codes,startDate,endDate):
        raw=[]
        for code in codes:
            data=self.getDataFromRedis(str(code))
            raw.append(data)
        raw=pd.concat(raw)
        raw=raw[(raw['date']>=str(startDate)) & (raw['date']<=str(endDate))]
        raw['winnum']=raw['winRate']*raw['count']
        raw['totalamount']=raw['dailyOpen']*raw['originalPosition']
        prepared=raw.groupby(by=['parameter1','parameter2'])['profit'].sum()
        prepared=pd.DataFrame(prepared)
        prepared['long']=raw.groupby(by=['parameter1','parameter2'])['long'].sum()
        prepared['short']=raw.groupby(by=['parameter1','parameter2'])['short'].sum()
        prepared['winnum']=raw.groupby(by=['parameter1','parameter2'])['winnum'].sum()
        prepared['totalamount']=raw.groupby(by=['parameter1','parameter2'])['totalamount'].sum()
        prepared['amount']=raw.groupby(by=['parameter1','parameter2'])['amount'].sum()
        prepared['turnover']=prepared['amount']/prepared['totalamount']
        prepared['totalhold']=raw.groupby(by=['parameter1','parameter2'])['totalHold'].sum()
        prepared['totalnum']=prepared['long']+prepared['short']
        prepared['winrate']=prepared['winnum']/prepared['totalnum']
        prepared['hold']=prepared['totalhold']/prepared['totalnum']
        prepared['slip']=raw.groupby(by=['parameter1','parameter2'])['slip'].sum()
        prepared['netprofit']=prepared['profit']-prepared['slip']
        prepared=prepared.sort_values(['netprofit'])
        return prepared
        pass
    #----------------------------------------------------------------------
    def getBestPerformanceEachCode(self,code,startDate,endDate):
        answer=pd.DataFrame()
        data=self.getDataFromRedis(str(code))
        if data.shape[0]<=0:
            return answer
        value=data.values
        columnsIndex={}
        for i in range(len(data.columns)):
            columnsIndex.update({data.columns[i]:i})
        select= value[np.where((value[:,columnsIndex['code']]==code) & (value[:,columnsIndex['date']]>=str(startDate)) & (value[:,columnsIndex['date']]<=str(endDate)))]
        #for i in value.shape[0]:
        #    mycode=value[i][columnsIndex['code']]
        #    mydate=value[i][columnsIndex['date']]
        #    if ((mycode==code) and (mydate>=str(startDate)) and (mydate<=str(endDate))):
        #        pass
        #data=data[(data['code']==code) & (data['date']>=str(startDate)) & (data['date']<=str(endDate))]
        days=list(TradedayDataProcess.getTradedays(startDate, endDate))
        n=len(days)
        data=pd.DataFrame(select,columns=data.columns)
        result=data.groupby(by=['parameter1','parameter2'])['profit'].sum()
        result=pd.DataFrame(result,columns=['profit'])
        result=result[result['profit']>0]
        status=[]
        for myindex in result.index:
            myresult=data[(data['parameter1']==myindex[0]) &(data['parameter2']==myindex[1])]
            sharpe=myresult['yieldToAll'].sum()/myresult['yieldToAll'].std()*np.sqrt(252)
            mytrade=myresult[myresult['count']>0]
            mywinrate=(mytrade['winRate']*mytrade['count']).sum()/(mytrade['count'].sum())
            myturnover=mytrade['turnoverRate'].mean()
            mytotalturnover=myturnover*mytrade.shape[0]/myresult.shape[0]
            myhold=mytrade['hold'].mean()
            mylong=mytrade['long'].sum()
            myshort=mytrade['short'].sum()
            myyield=mytrade['yieldToAll'].sum()
            status.append({'code':code,'parameter1':myindex[0],'parameter2':myindex[1],'sharpe':sharpe,'winrate':mywinrate,'turnover':myturnover,'long':mylong,'short':myshort,'totalYield':myyield,'totalturnover':mytotalturnover,'count':mylong+myshort})
            pass
        status=pd.DataFrame(status)
        if status.shape[0]==0:
            return pd.DataFrame()
        #status=status[(status['totalturnover']>0.05) &(status['winrate']>0.5)]
        if status.shape[0]==0:
            return pd.DataFrame()
        status=status.sort_values(by=['sharpe'],ascending=False)
        ##data[(data['parameter1']==result.index[50][0]) &(data['parameter2']==result.index[50][1])]
        #result=pd.DataFrame(result,columns=['yieldToAll'])
        #result['profit']=data.groupby(by=['parameter1','parameter2'])['profit'].sum()
        #result['winRate']=data.groupby(by=['parameter1','parameter2'])['winRate'].sum()/n
        #result=result.reset_index()
        #select=result['profit']>0
        #result['modifiedProfit']=0
        #result.loc[select,'modifiedProfit']=result['profit'][select]
        ##result=result[(result['parameter1']<0.005) & (result['parameter2']<0.5)]
        #result['modifiedProfit']=np.log(result['modifiedProfit']+1)
        #result['score']=result['profit']
        #result=result.sort_values(by=['score'],ascending=False)
        if status.shape[0]>0:
            answer=status.iloc[0:1]
            #parameter1=stauts['parameter1'].iloc[0]
            #parameter2=stauts['parameter2'].iloc[0]
            #answer=pd.DataFrame([{'code':code,'parameter1':parameter1,'parameter2':parameter2}])
        #print(f"{code} complete!")
        return answer
        pass
    #----------------------------------------------------------------------
    def getProfitEachCodeByParameters(self,code,parameter1,parameter2,startDate,endDate):
        answer=pd.DataFrame()
        data=self.getDataFromRedis(str(code))
        if data.shape[0]<=0:
            return answer
        value=data.values
        columnsIndex={}
        for i in range(len(data.columns)):
            columnsIndex.update({data.columns[i]:i})
        select= value[np.where((value[:,columnsIndex['code']]==code) & (value[:,columnsIndex['date']]>=str(startDate)) & (value[:,columnsIndex['date']]<=str(endDate)) & (abs(value[:,columnsIndex['parameter1']]-parameter1)<0.0001) & (abs(value[:,columnsIndex['parameter2']]-parameter2)<0.0001) )]
        data=pd.DataFrame(select,columns=data.columns)
        #data=data[(data['code']==code) & (data['date']>=str(startDate)) & (data['date']<=str(endDate))]
        #data=data[(abs(data['parameter1']-parameter1)<0.0001)& (abs(data['parameter2']-parameter2)<0.0001)]
        days=list(TradedayDataProcess.getTradedays(startDate, endDate))
        n=len(days)
        profit=data['profit'].sum()/n
        yieldToAll=data['yieldToAll'].sum()/n
        answer=pd.DataFrame([{'code':code,'parameter1':parameter1,'parameter2':parameter2,'tradedays':n,'profit_mean':profit,'yield_mean':yieldToAll}])
        #print(f"{code} complete!")
        return answer
    #----------------------------------------------------------------------
    def getEveryCodeBestPerformance(self,codes,startDate,endDate):
        allResults=Parallel(n_jobs=MYJOBS,verbose=0)(delayed(self.getBestPerformanceEachCode)(code,startDate,endDate) for code in codes)
        data=pd.concat(allResults)
        return data
        pass
    #----------------------------------------------------------------------
    def getEveryCodeProfitByParameters(self,parameters,startDate,endDate):
        columnsIndex={}
        for i in range(len(parameters.columns)):
            columnsIndex.update({parameters.columns[i]:i})
        p=parameters.values
        allResults=Parallel(n_jobs=MYJOBS,verbose=0)(delayed(self.getProfitEachCodeByParameters)(p[i][columnsIndex['code']],p[i][columnsIndex['parameter1']],p[i][columnsIndex['parameter2']],startDate,endDate) for i in range(p.shape[0]))
        data=pd.concat(allResults)
        return data
        pass
    #----------------------------------------------------------------------
    def getRollingProfit(self,codes,startDate,endDate,testdays,profitdays):
        days=list(TradedayDataProcess.getTradedays(startDate, endDate))
        result=[]
        warnings.filterwarnings('ignore')
        for i in range(testdays,len(days),profitdays):
            teststart=days[i-testdays]
            testend=days[i-1]
            mystart=days[i]
            myend=days[min(i+profitdays-1,len(days)-1)]
            print(f"test:{teststart}-{testend},profit:{mystart}-{myend}")
            r=self.getEveryCodeBestPerformance(codes,teststart,testend)
            rr=self.getEveryCodeProfitByParameters(r,mystart,myend)
            rr['profit']=rr['profit_mean']*rr['tradedays']
            profit=rr['profit'].sum()
            print(f"test:{teststart}-{testend},profit:{mystart}-{myend},{profit}")
            result.append({'teststart':teststart,'testend':testend,'mystart':mystart,'myend':myend,'profit':profit})
            pass
        result=pd.DataFrame(result)
        #print(result)
        return result
        pass
    #----------------------------------------------------------------------
    def getRollingProfitBySameParameters(self,codes,startDate,endDate,testdays,profitdays):
        days=list(TradedayDataProcess.getTradedays(startDate, endDate))
        result=[]
        warnings.filterwarnings('ignore')
        for i in range(testdays,len(days),profitdays):
            teststart=days[i-testdays]
            testend=days[i-1]
            mystart=days[i]
            myend=days[min(i+profitdays-1,len(days)-1)]
            print(f"test:{teststart}-{testend},profit:{mystart}-{myend}")
            r=self.getBestPerformanceByCodeGroup(codes,teststart,testend)
            parameter1=r.index[-1][0]
            parameter2=r.index[-1][1]
            rr=self.getBestPerformanceByCodeGroup(codes,mystart,myend)
            rr=rr.reset_index()
            rr=rr[(rr['parameter1']==parameter1) & (rr['parameter2']==parameter2)]
            rr['teststart']=teststart
            rr['testend']=testend
            rr['mystart']=mystart
            rr['myend']=myend
            profit=rr['profit'].sum()
            netprofit=rr['netprofit'].sum()
            print(f"test:{teststart}-{testend},profit:{mystart}-{myend},{profit},{netprofit}")
            result.append(rr.iloc[0:1])
            pass
        result=pd.concat(result)
        #print(result)
        return result
        pass
########################################################################
